﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a sales option.
    /// </summary>
    public class CreateSalesOption : CreateAuditable<SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string CodeName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string CodeValue
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price.
        /// </summary>
        public decimal Price
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public SalesOptionFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(CodeName))
                return Err.Of<SalesOptionEntity>().NotValid("CodeName");
            if(String.IsNullOrWhiteSpace(CodeValue))
                return Err.Of<SalesOptionEntity>().NotValid("CodeValue");
            return base.Validate(uow);
        }

        protected override void Construct(IUnitOfWork uow, SalesOptionEntity entity)
        {
            base.Construct(uow, entity);

            entity.CodeName = CodeName.TrimToLen(10);
            entity.CodeValue = CodeValue.TrimToLen(10);
            entity.Price = Price;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the sales option.
    /// </summary>
    public class UpdateSalesOption : UpdateAuditable<SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the code name.
        /// </summary>
        public string CodeName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the code value.
        /// </summary>
        public string CodeValue
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price.
        /// </summary>
        public decimal Price
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public SalesOptionFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(CodeName))
                return Err.Of<SalesOptionEntity>().NotValid("CodeName");
            if(String.IsNullOrWhiteSpace(CodeValue))
                return Err.Of<SalesOptionEntity>().NotValid("CodeValue");
            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, SalesOptionEntity entity)
        {
            base.Revise(uow, entity);

            entity.CodeName = CodeName.TrimToLen(10);
            entity.CodeValue = CodeValue.TrimToLen(10);
            entity.Price = Price;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the sales option.
    /// </summary>
    public class DeleteSalesOption : DeleteEntity<SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }
        
        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, SalesOptionEntity entity)
        {
            foreach(var i in entity.Translations)
            {
                uow.Execute(new DeleteSalesOptionTranslation
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.SalesItems)
            {
                uow.Execute(new UnmapSalesOptionFromSalesItem
                {
                    EntityID = i.EntityID,
                    ChildID = entity.EntityID
                });
            }
            foreach(var i in entity.ShoppingCarts)
            {
                uow.Execute(new UnmapSalesOptionFromShoppingCart
                {
                    EntityID = i.EntityID,
                    ChildID = entity.EntityID
                });
            }
            foreach(var i in entity.ShoppingCartItems)
            {
                uow.Execute(new UnmapSalesOptionFromShoppingCartItem
                {
                    EntityID = i.EntityID,
                    ChildID = entity.EntityID
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the sales option by identifier.
    /// </summary>
    public class GetSalesOption : GetEntity<SalesOptionEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Translation___

    /// <summary>
    /// Creates an option translation.
    /// </summary>
    public class CreateSalesOptionTranslation : CreateTranslation
        <TSalesOptionEntity, SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        public string Value
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, TSalesOptionEntity entity)
        {
            base.Construct(uow, entity);

            entity.Value = StringHelper.EnsureNotNull(Value).TrimToLen(100);
            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Updates the option translation.
    /// </summary>
    public class UpdateSalesOptionTranslation : UpdateTranslation
        <TSalesOptionEntity, SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        public string Value
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, TSalesOptionEntity entity)
        {
            base.Revise(uow, entity);

            entity.Value = StringHelper.EnsureNotNull(Value).TrimToLen(100);
            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Saves an option translation.
    /// </summary>
    public class SaveSalesOptionTranslation : SaveTranslation
        <TSalesOptionEntity, SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        public string Value
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out TSalesOptionEntity result)
        {
            var translation = uow.Execute(new GetSalesOptionTranslationByLanguage
            {
                HolderID = HolderID,
                LanguageID = LanguageID
            }).Result;
            if(translation == null)
            {
                translation = uow.Execute(new CreateSalesOptionTranslation
                {
                    HolderID = HolderID,
                    LanguageID = LanguageID,
                    Name = Name,
                    Value = Value
                }).Result;
            }
            else
            {
                translation = uow.Execute(new UpdateSalesOptionTranslation
                {
                    EntityID = translation.EntityID,
                    LanguageID = LanguageID,
                    Name = Name,
                    Value = Value
                }).Result;
            }
            result = translation;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the option translation.
    /// </summary>
    public class DeleteSalesOptionTranslation : DeleteTranlsation
        <TSalesOptionEntity, SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the option translation.
    /// </summary>
    public class GetSalesOptionTranslation : GetTranlsation
        <TSalesOptionEntity, SalesOptionEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the option translation.
    /// </summary>
    public class GetSalesOptionTranslationByLanguage : GetTranlsationByLanguage
        <TSalesOptionEntity, SalesOptionEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over sales option collection.
    /// </summary>
    public class SearchSalesOptions : SearchOperation<SalesOptionEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchSalesOptions()
        {
            Term = SearchTerm.Empty;
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                SalesOptionSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int? LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the supplier identifier.
        /// </summary>
        public int? ItemID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public SalesOptionFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public SalesOptionSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<SalesOptionEntity> Build(IUnitOfWork uow, IQueryable<SalesOptionEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.StartsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.StartsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Value.EndsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Value.EndsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Contains:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            (i.Value.Contains(Term.Keyword) ||
                            i.Name.Contains(Term.Keyword))));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Value.Contains(Term.Keyword) ||
                            i.Name.Contains(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Equals:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Value.Equals(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Value.Equals(Term.Keyword)));
                    break;
                default:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i => i.Language.EntityID == LanguageID.Value));
                    break;
            }
            if(ItemID.HasValue)
                query = query.Where(x => x.SalesItems.Any(i => i.EntityID == ItemID.Value));
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case SalesOptionSortBy.CodeName:
                            query = query.OrderBy(x => x.CodeName, SortDir, i != 0);
                            break;
                        case SalesOptionSortBy.CodeValue:
                            query = query.OrderBy(x => x.CodeValue, SortDir, i != 0);
                            break;
                        case SalesOptionSortBy.Price:
                            query = query.OrderBy(x => x.Price, SortDir, i != 0);
                            break;
                        case SalesOptionSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the collection of attributes as a dictionary, where 
    /// key - attribute identifier and value - attribute name.
    /// </summary>
    public class GetSalesOptionDictionary : SearchOperation<TSalesOptionEntity, Int32, String>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public GetSalesOptionDictionary()
        {
            Term = SearchTerm.Empty;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public SalesOptionFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<TSalesOptionEntity> Build(IUnitOfWork uow, IQueryable<TSalesOptionEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Holder.Flags, (int)FlagMask);
            query = query.Where(x => x.Language.EntityID == LanguageID);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x => x.Name.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x => x.Value.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x =>
                        x.Value.Contains(Term.Keyword) ||
                        x.Name.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x => x.Value.Equals(Term.Keyword));
                    break;
            }
            query = query.OrderBy(x => x.Value);
            return query;
        }

        protected override IQueryable<KeyValuePair<Int32, String>> Map(IQueryable<TSalesOptionEntity> query)
        {
            return query.Select(x => new KeyValuePair<Int32, String>(x.Holder.EntityID, String.Format("{0}: {1}", x.Name, x.Value)));
        }

        #endregion
    }

    #endregion
}
